#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip_icmp.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <time.h>

#define ICMP_ECHO_REQUEST 8
#define ICMP_ECHO_REPLY   0
#define PACKET_SIZE       64

// 计算ICMP校验和
unsigned short cal_checksum(unsigned short *buf, int len) {
    unsigned long sum = 0;
    while (len > 1) {
        sum += *buf++;
        len -= 2;
    }
    if (len == 1) {
        sum += *(unsigned char *)buf;
    }
    sum = (sum >> 16) + (sum & 0xffff);
    sum += (sum >> 16);
    return (unsigned short)(~sum);
}

int main(int argc, char *argv[]) {
    if (argc != 2) {
        printf("Usage: %s <destination_ip>\n", argv[0]);
        exit(1);
    }

    int sockfd;
    struct sockaddr_in dest_addr;
    struct icmp *icmp_packet;
    char packet[PACKET_SIZE];
    int n;
    socklen_t addr_len;
    struct timeval tv;

    // 创建原始套接字
    sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
    if (sockfd < 0) {
        perror("socket error");
        exit(1);
    }

    // 设置接收超时
    tv.tv_sec = 1;
    tv.tv_usec = 0;
    setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));

    // 初始化目标地址
    memset(&dest_addr, 0, sizeof(dest_addr));
    dest_addr.sin_family = AF_INET;
    dest_addr.sin_addr.s_addr = inet_addr(argv[1]);

    // 构造ICMP回显请求包
    icmp_packet = (struct icmp *)packet;
    icmp_packet->icmp_type = ICMP_ECHO_REQUEST;
    icmp_packet->icmp_code = 0;
    icmp_packet->icmp_cksum = 0;
    icmp_packet->icmp_id = getpid();
    icmp_packet->icmp_seq = 1;
    memset(icmp_packet->icmp_data, 'a', PACKET_SIZE - sizeof(struct icmp));
    icmp_packet->icmp_cksum = cal_checksum((unsigned short *)icmp_packet, PACKET_SIZE);

    // 发送ICMP请求
    if (sendto(sockfd, packet, PACKET_SIZE, 0, (struct sockaddr *)&dest_addr, sizeof(dest_addr)) < 0) {
        perror("sendto error");
        exit(1);
    }

    // 接收ICMP应答
    addr_len = sizeof(dest_addr);
    n = recvfrom(sockfd, packet, PACKET_SIZE, 0, (struct sockaddr *)&dest_addr, &addr_len);
    if (n < 0) {
        printf("Request timed out.\n");
    } else {
        struct icmp *recv_icmp = (struct icmp *)(packet + sizeof(struct ip));
        if (recv_icmp->icmp_type == ICMP_ECHO_REPLY && recv_icmp->icmp_id == getpid()) {
            printf("Reply from %s: bytes=%d time=%.3f ms\n", 
                   argv[1], n, (float)(tv.tv_sec * 1000 + tv.tv_usec / 1000));
        }
    }

    close(sockfd);
    return 0;
}